home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ue312src.zip / MSWMENU.C < prev    next >
C/C++ Source or Header  |  1992-10-23  |  42KB  |  1,369 lines

  1. /* The routines in this file provide menu-related functions under the
  2.    Microsoft Windows environment on an IBM-PC or compatible computer.
  3.  
  4.    This module also contains the code for the About and Modes dialog
  5.    boxes.
  6.  
  7.    Must be compiled with Borland C++ 2.0 or MSC 6.0 or later versions.
  8.  
  9.    It should not be compiled if the WINDOW_MSWIN symbol is not set */
  10.  
  11. #include    "estruct.h"
  12. #include    "elang.h"
  13. #include    <stdio.h>
  14. #include    "eproto.h"
  15. #include    "edef.h"
  16.  
  17. #include    "mswin.h"
  18. #include    "mswmenu.h"
  19. #include    "mswhelp.h"
  20.  
  21. #define MAXMENUTITLE 50
  22.  
  23. /* codes for the MenuType parameter from AddMenuEntry () */
  24. #define MT_DUMMY    1
  25. #define MT_MENUBAR  2
  26.  
  27. typedef struct   {
  28.     WORD    m_word;
  29.     ETYPE EPOINTER m_ptr;
  30. } MENUTAB;
  31.  
  32. #define MB_FNC  0x4000
  33. #define MB_BUF  0x8000  /* static IDs should not look like they carry
  34.                this flag */
  35.  
  36. #define MAXDYNMENU  256
  37.  
  38. static MENUTAB MenuDynBind [MAXDYNMENU] = {0, NULL};
  39.  
  40. static MENUTAB MenuStaticBind [] = {
  41.     {IDM_NULLPROC,      nullproc},
  42.     {IDM_FILEFIND,      filefind},
  43.     {IDM_VIEWFILE,      viewfile},
  44.     {IDM_INSFILE,    insfile},
  45.     {IDM_FILEREAD,    fileread},
  46.     {IDM_FILENAME,    filename},
  47.     {IDM_FILESAVE,    filesave},
  48.     {IDM_FILEWRITE,    filewrite},
  49.     {IDM_FILEAPP,    fileapp},
  50.     {IDM_SETEKEY,    setekey},
  51.     {IDM_NEXTBUFFER,    nextbuffer},
  52.     {IDM_USEBUFFER,    usebuffer},
  53.     {IDM_UNMARK,    unmark},
  54.     {IDM_NAMEBUFFER,    namebuffer},
  55.     {IDM_KILLBUFFER,    killbuffer},
  56.     {IDM_NARROW,    narrow},
  57.     {IDM_WIDEN,        widen},
  58.     {IDM_LISTBUFFERS,    listbuffers},
  59.     {IDM_SPLITWIND,    splitwind},
  60.     {IDM_DELWIND,    delwind},
  61.     {IDM_ONLYWIND,    onlywind},
  62.     {IDM_NEXTWIND,    nextwind},
  63.     {IDM_PREVWIND,    prevwind},
  64.     {IDM_MVUPWIND,    mvupwind},
  65.     {IDM_MVDNWIND,    mvdnwind},
  66.     {IDM_NEXTUP,    nextup},
  67.     {IDM_NEXTDOWN,    nextdown},
  68.     {IDM_ENLARGEWIND,    enlargewind},
  69.     {IDM_SHRINKWIND,    shrinkwind},
  70.     {IDM_RESIZE,    resize},
  71.     {IDM_QUICKEXIT,    quickexit},
  72.     {IDM_QUIT,        quit},
  73.         /* here just to compute the key binding: the actual IDM_QUIT
  74.        selection is processed as a special */
  75.     {IDM_CUTREGION,    cutregion},
  76.     {IDM_CLIPREGION,    clipregion},
  77.     {IDM_INSERTCLIP,    insertclip},
  78.     {IDM_SETMARK,    setmark},
  79.     {IDM_REMMARK,    remmark},
  80.     {IDM_SWAPMARK,    swapmark},
  81.     {IDM_YANK,        yank},
  82.     {IDM_KILLREGION,    killregion},
  83.     {IDM_COPYREGION,    copyregion},
  84.     {IDM_UPPERREGION,    upperregion},
  85.     {IDM_LOWERREGION,    lowerregion},
  86.     {IDM_ENTAB,        entab},
  87.     {IDM_DETAB,        detab},
  88.     {IDM_TRIM,        trim},
  89.     {IDM_INDENT_REGION,    indent_region},
  90.     {IDM_UNDENT_REGION,    undent_region},
  91.     {IDM_WORDCOUNT,    wordcount},
  92.     {IDM_FILLPARA,    fillpara},
  93.     {IDM_KILLPARA,    killpara},
  94.     {IDM_KILLTEXT,    killtext},
  95.     {IDM_OPENLINE,    openline},
  96.     {IDM_DELFWORD,    delfword},
  97.     {IDM_DELBWORD,    delbword},
  98.     {IDM_CAPWORD,    capword},
  99.     {IDM_LOWERWORD,    lowerword},
  100.     {IDM_UPPERWORD,    upperword},
  101.     {IDM_DEBLANK,    deblank},
  102.     {IDM_TWIDDLE,    twiddle},
  103.     {IDM_TAB,        tab},
  104.     {IDM_QUOTE,        quote},
  105.     {IDM_SETFILLCOL,    setfillcol},
  106.     {IDM_FORWSEARCH,    forwsearch},
  107.     {IDM_BACKSEARCH,    backsearch},
  108.     {IDM_FORWHUNT,    forwhunt},
  109.     {IDM_BACKHUNT,    backhunt},
  110.     {IDM_FISEARCH,    fisearch},
  111.     {IDM_RISEARCH,    risearch},
  112.     {IDM_SREPLACE,    sreplace},
  113.     {IDM_QREPLACE,    qreplace},
  114.     {IDM_GOTOMARK,    gotomark},
  115.     {IDM_GOTOLINE,    gotoline},
  116.     {IDM_GETFENCE,    getfence},
  117.     {IDM_GOTOBOB,    gotobob},
  118.     {IDM_GOTOEOB,    gotoeob},
  119.     {IDM_FORWPAGE,    forwpage},
  120.     {IDM_BACKPAGE,    backpage},
  121.     {IDM_GOTOEOP,    gotoeop},
  122.     {IDM_GOTOBOP,    gotobop},
  123.     {IDM_FORWLINE,    forwline},
  124.     {IDM_BACKLINE,    backline},
  125.     {IDM_GOTOBOL,    gotobol},
  126.     {IDM_GOTOEOL,    gotoeol},
  127.     {IDM_FORWWORD,    forwword},
  128.     {IDM_BACKWORD,    backword},
  129.     {IDM_ENDWORD,    endword},
  130.     {IDM_EXECPRG,    execprg},
  131.     {IDM_SPAWNCLI,    spawncli},
  132.     {IDM_SPAWN,        spawn},
  133.     {IDM_PIPECMD,    pipecmd},
  134.     {IDM_FILTER,    filter},
  135.     {IDM_CTLXE,        ctlxe},
  136.     {IDM_CTLXLP,    ctlxlp},
  137.     {IDM_CTLXRP,    ctlxrp},
  138.     {IDM_NAMEDCMD,    namedcmd},
  139.     {IDM_EXECCMD,    execcmd},
  140.     {IDM_EXECPROC,    execproc},
  141.     {IDM_EXECBUF,    execbuf},
  142.     {IDM_EXECFILE,    execfile},
  143.     {IDM_BINDTOKEY,    bindtokey},
  144.     {IDM_MACROTOKEY,    macrotokey},
  145.     {IDM_BINDTOMENU,    bindtomenu},
  146.     {IDM_MACROTOMENU,    macrotomenu},
  147.     {IDM_UNBINDKEY,    unbindkey},
  148.     {IDM_UNBINDMENU,    unbindmenu},
  149.     {IDM_DESKEY,    deskey},
  150.     {IDM_SETVAR,    setvar},
  151.     {IDM_DISPVAR,    dispvar},
  152.     {IDM_DESVARS,    desvars},
  153.     {IDM_SHOWCPOS,    showcpos},
  154.     {IDM_CTRLG,        ctrlg},
  155.     {IDM_FIND_SCREEN,    find_screen},
  156.     {IDM_RENAME_SCREEN,    rename_screen},
  157.     {IDM_NEWSIZE,    newsize},
  158.     {IDM_NEWWIDTH,    newwidth},
  159.     {IDM_DESBIND,    desbind},
  160.     {IDM_DESFUNC,    desfunc},
  161.     {IDM_APRO,        apro},
  162.     {0,                 NULL}
  163. };
  164.  
  165. #define MAXMENUPARENT   7
  166.  
  167. typedef struct {    /* current menu description */
  168.     int     cm_pos;     /* entry position */
  169.     int     cm_x;       /* index of the last parent handle, -1 if entry
  170.                located in main menu bar */
  171.     HMENU   cm_parent[MAXMENUPARENT];
  172.                         /* parent handle, main popup menu at index 0 */
  173. } CURMENU;
  174.  
  175. static CURMENU  CurrentMenu = {-1, -1, {NULL}};
  176.  
  177. static unsigned int ctrlx_key = 0;
  178. static unsigned int meta_key = 0;       /* for GetKeyText */
  179.  
  180. /* execmenu:    execute a function bound to a menu */
  181. /* ========                                        */
  182.  
  183. /* This function is called by the edit loop in main.c, when a MENU
  184.    extended character is detected. */
  185.  
  186. PASCAL execmenu (int f, int n)
  187. /* f, n: arguments to target function */
  188. {
  189.     register UINT    ID;
  190.     register MENUTAB *MTp;
  191.  
  192.     ID = (xpos << 8) + ypos;    /* getkey sees MENU sequences as MOUS */
  193.     if (ID >= IDM_DYNAMIC) {
  194.         MTp = &MenuDynBind[ID - IDM_DYNAMIC];
  195.         if (MTp->m_word & MB_FNC) return (*(MTp->m_ptr.fp)) (f, n);
  196.         else if (MTp->m_word & MB_BUF) {
  197.             BOOL    status;
  198.  
  199.             if (f == FALSE) n = 1;
  200.             while (n--) {
  201.                 status = dobuf (MTp->m_ptr.buf);
  202.                 if (status != TRUE) return status;
  203.             }
  204.         }
  205.         else return FAILED;
  206.         return TRUE;
  207.     }
  208.     else for (MTp = &MenuStaticBind[0]; MTp->m_word != 0; MTp++) {
  209.         if (ID == MTp->m_word) return (*(MTp->m_ptr.fp)) (f, n);
  210.     }
  211.     return FAILED;      /* not found !!! */
  212. } /* execmenu */
  213.  
  214. /* GenerateMenuSeq: send a menu sequence into the input stream */
  215. /* ===============                                             */
  216.  
  217. void FAR PASCAL GenerateMenuSeq (UINT ID)
  218. {
  219.     if (!in_room (5)) return;
  220.     in_put (0);             /* escape indicator */
  221.     in_put (MENU >> 8);     /* MENU prefix */
  222.     in_put (ID >> 8);       /* menu ID high byte (--> xpos) */
  223.     in_put (ID & 0xFF);     /* menu ID low byte (--> ypos) */
  224.     in_put ('m');           /* dummy event */
  225. } /* GenerateMenuSeq */
  226.  
  227. /* AboutDlgProc:  About box dialog function */
  228. /* ============                             */
  229. int EXPORT FAR PASCAL  AboutDlgProc (HWND hDlg, UINT wMsg, UINT wParam,
  230.                                      LONG lParam)
  231. {
  232.     char    s [50];
  233.     static RECT FullBox;
  234.     
  235.     switch (wMsg) {
  236.         
  237.     case WM_INITDIALOG:
  238.         strcpy (s, PROGNAME);
  239.         strcat (s, " ");
  240.         strcat (s, VERSION);
  241.         SetDlgItemText (hDlg, ID_PROGVER, s);
  242.         {
  243.             RECT    Bar;
  244.  
  245.         GetWindowRect (hDlg, &FullBox);
  246.             GetWindowRect (GetDlgItem (hDlg, ID_ABOUTBAR), &Bar);
  247.             MoveWindow (hDlg, FullBox.left, FullBox.top,
  248.                         Bar.left + GetSystemMetrics (SM_CXDLGFRAME)
  249.                                  - FullBox.left,
  250.                         FullBox.bottom - FullBox.top,
  251.                         FALSE);     /* shrink dialog box, do not repaint */
  252.         }
  253.     return TRUE;
  254.     
  255.     case WM_COMMAND:
  256.     if (NOTIFICATION_CODE != BN_CLICKED) break;
  257.     if (LOWORD(wParam) == 1) {
  258.         EndDialog (hDlg, 0);
  259.         return TRUE;
  260.     }
  261.     else if (LOWORD(wParam) == ID_MOREABOUT) {
  262.         EnableWindow ((HWND)lParam, FALSE);   /* disable "More >>" */
  263.         SetFocus (GetDlgItem (hDlg, 1));         /* make "OK" default */
  264.         MoveWindow (hDlg, FullBox.left, FullBox.top,
  265.                     FullBox.right - FullBox.left,
  266.                         FullBox.bottom - FullBox.top,
  267.                         TRUE);      /* enlarge dialog box and repaint */
  268.         return TRUE;
  269.     }
  270.     break;
  271.     default:
  272.     return FALSE;
  273.     }
  274.     return FALSE;
  275. } /* AboutDlgProc */
  276.  
  277. /* SetCheck:    puts a check mark in a check box */
  278. /* ========                                      */
  279.  
  280. void  PASCAL    SetCheck (HWND hDlg, int BoxID)
  281. {
  282.     SendMessage (GetDlgItem (hDlg, BoxID), BM_SETCHECK, 1, 0L);
  283. } /* SetCheck */
  284.  
  285. /* GetCheck:    TRUE is the check box is checked */
  286. /* ========                                      */
  287.  
  288. BOOL  PASCAL    GetCheck (HWND hDlg, int BoxID)
  289. {
  290.     return (SendMessage (GetDlgItem (hDlg, BoxID), BM_GETCHECK, 0, 0L) != 0);
  291. } /* GetCheck */
  292.  
  293. /* ModeDlgProc: Modes dialog box function */
  294. /* ===========                            */
  295.  
  296. /* must be invoked through DialogBoxParam, with LOWORD(dwInitParam) set
  297.    to TRUE for global modes and FALSE for current buffer modes */
  298.  
  299. int EXPORT FAR PASCAL  ModeDlgProc (HWND hDlg, UINT wMsg, UINT wParam,
  300.                                     LONG lParam)
  301. {
  302.     char    s[40+NBUFN];
  303.     static int *modep;      /* mode flags pointer */
  304.     
  305.     switch (wMsg) {
  306.         
  307.     case WM_INITDIALOG:
  308.         if (LOWORD(lParam)) {
  309.         strcpy (s, TEXT331);    /* "Global modes" */
  310.         modep = &gmode;
  311.     }
  312.         else {
  313.         strcpy (s, TEXT332);    /* "Modes for buffer: " */
  314.         strcat (s, curbp->b_bname);
  315.         modep = &(curbp->b_mode);
  316.     }
  317.         SetWindowText (hDlg, s);
  318.         if (*modep & MDWRAP) SetCheck (hDlg, ID_WRAP);
  319.     if (*modep & MDCMOD) SetCheck (hDlg, ID_CMODE);
  320.         if (*modep & MDEXACT) SetCheck (hDlg, ID_EXACT);
  321.         if (*modep & MDVIEW) SetCheck (hDlg, ID_VIEW);
  322.         if (*modep & MDOVER) SetCheck (hDlg, ID_OVER);
  323.         if (*modep & MDREPL) SetCheck (hDlg, ID_REP);
  324.         if (*modep & MDMAGIC) SetCheck (hDlg, ID_MAGIC);
  325.         if (*modep & MDCRYPT) SetCheck (hDlg, ID_CRYPT);
  326.         if (*modep & MDASAVE) SetCheck (hDlg, ID_ASAVE);
  327.     return TRUE;
  328.     
  329.     case WM_COMMAND:
  330.     if (NOTIFICATION_CODE != BN_CLICKED) break;
  331.     switch (LOWORD(wParam)) {
  332.         
  333.     case 1:        /* OK */
  334.         *modep = 0;
  335.         if (GetCheck (hDlg, ID_WRAP)) *modep |= MDWRAP;
  336.         if (GetCheck (hDlg, ID_CMODE)) *modep |= MDCMOD;
  337.         if (GetCheck (hDlg, ID_EXACT)) *modep |= MDEXACT;
  338.         if (GetCheck (hDlg, ID_VIEW)) *modep |= MDVIEW;
  339.         if (GetCheck (hDlg, ID_OVER)) *modep |= MDOVER;
  340.         if (GetCheck (hDlg, ID_REP)) *modep |= MDREPL;
  341.         if (GetCheck (hDlg, ID_MAGIC)) *modep |= MDMAGIC;
  342.         if (GetCheck (hDlg, ID_CRYPT)) *modep |= MDCRYPT;
  343.         if (GetCheck (hDlg, ID_ASAVE)) *modep |= MDASAVE;
  344.         EndDialog (hDlg, 0);
  345.         break;
  346.         
  347.     case 2:        /* Cancel */
  348.         EndDialog (hDlg, -1);
  349.         break;
  350.     
  351.     case ID_OVER:
  352.         /* if OVER set, clear the REP mode check-box */
  353.         if (GetCheck (hDlg, ID_OVER)) {
  354.         SendMessage (GetDlgItem (hDlg, ID_REP), BM_SETCHECK, 0, 0L);
  355.         }
  356.         break;
  357.  
  358.     case ID_REP:
  359.         /* if REP set, clear the OVER mode check-box */
  360.         if (GetCheck (hDlg, ID_REP)) {
  361.         SendMessage (GetDlgItem (hDlg, ID_OVER), BM_SETCHECK, 0, 0L);
  362.         }
  363.         break;
  364.  
  365.     }
  366.     break;
  367.  
  368.     default:
  369.     return FALSE;
  370.     }
  371.     return FALSE;
  372. } /* ModeDlgProc */
  373.  
  374. /* IsMenuSeparator:  TRUE if the item is a separator */
  375. /* ===============                                   */
  376. static BOOL PASCAL IsMenuSeparator (HMENU hMenu, int Position)
  377. {
  378.     DWORD state;
  379.     
  380.     if (GetSubMenu (hMenu, Position) != 0) return FALSE;
  381.     if ((state = GetMenuState (hMenu, Position, MF_BYPOSITION)) != -1) {
  382.         if (state & MF_SEPARATOR) return TRUE;
  383.     }
  384.     return FALSE;
  385. } /* IsMenuSeparator */
  386.  
  387. /* GetMenuEntryID:   give back ID of MenuItem or popup */
  388. /* ==============                                      */
  389.  
  390. /* if the entry is a menu item (not a popup), its ID is returned. If the
  391.    entry is a popup, the ID of the first entry in this popup is
  392.    returned, added to n*IDM_POPUP where n is the number of popup levels.
  393.  
  394.    If the entry is neither an item nor a popup, an item id of 0 is
  395.    returned */
  396.  
  397. UINT  PASCAL    GetMenuEntryID (HMENU hMenu, int Position)
  398. {
  399.     UINT    id;
  400.     HMENU   hSubMenu;
  401.  
  402.     if (IsMenuSeparator (hMenu, Position)) {
  403.         id = 0;
  404.     }
  405.     else if ((hSubMenu = GetSubMenu (hMenu, Position)) != 0) {
  406.     id = IDM_POPUP + GetMenuEntryID (hSubMenu, 0);
  407.     }
  408.     else id = GetMenuItemID (hMenu, Position);
  409.     return id;
  410. } /* GetMenuEntryID */
  411.  
  412. /* FindKeyBinding:  scan the key binding table, return first match */
  413. /* ==============                                                  */
  414.  
  415. /* If there is no match, the returned value point to the BINDNUL entry
  416.    */
  417.  
  418. KEYTAB * FAR PASCAL FindKeyBinding (void *Func)
  419. {
  420.     register KEYTAB *KTp;
  421.  
  422.     for (KTp = &keytab[0]; KTp->k_type != BINDNUL; ++KTp) {
  423.     if (((KTp->k_type == BINDFNC) && (KTp->k_ptr.fp == Func)) ||
  424.         ((KTp->k_type == BINDBUF) && (KTp->k_ptr.buf == Func)))
  425.         if (!(KTp->k_code & MOUS)) break;   /* found it! (we skip
  426.                            mouse bindings) */
  427.     }
  428.     return KTp;
  429.     
  430. } /* FindKeyBinding */
  431.  
  432. /* GetKeyText:  translates a key code into a CUA-type description */
  433. /* ==========                                                     */
  434.  
  435. /* Returns the length (excluding the terminating NULL) of the text if it
  436.    fits in the supplied buffer, 0 otherwise (in which case the buffer
  437.    contents are unuseable). */
  438.  
  439. /* This function uses the ctrlx_key and meta_key static variables to
  440.    avoid scanning the key binding table too often. If these are NULL,
  441.    however, it looks them up. Therefore, these variables can be zeroed
  442.    if there is a doubt about their validity */
  443.  
  444. int  PASCAL GetKeyText (int Key, char *Text, int TextLength)
  445. {
  446.     int     i;      /* index in Text */
  447.     char    c;
  448.  
  449.     if (Key & (CTLX | META)) {
  450.         unsigned *prefix_key_ptr;
  451.         int     n;
  452.  
  453.         prefix_key_ptr = (Key & CTLX) ? &ctrlx_key : &meta_key;
  454.     if (*prefix_key_ptr == 0) {
  455.         KEYTAB  *KTp;
  456.  
  457.         KTp = FindKeyBinding ((Key & CTLX) ? cex : meta);
  458.         if (KTp->k_type == BINDNUL) return 0;
  459.         *prefix_key_ptr = KTp->k_code;
  460.     }
  461. #define NBSPACES    3   /* # of spaces after prefix */
  462.     i = GetKeyText (*prefix_key_ptr, Text, TextLength - NBSPACES);
  463.     if (i == 0)  return 0;
  464.     for (n = 0; n < NBSPACES; ++n) Text[i++] = ' ';
  465.     }
  466.     else {
  467.     i = 0;
  468.     }
  469.  
  470. #define APPENDTEXT(s)   {if (TextLength - i < sizeof(s)) return 0;\
  471.                          strcpy (&Text[i], s); i += sizeof(s) - 1;}
  472.  
  473.     c = (char)Key;
  474.     
  475.     if (Key & ALTD) APPENDTEXT(TEXT310) /* "Alt+" */
  476.     
  477.     if (Key & SHFT) APPENDTEXT(TEXT311) /* "Shift+" */
  478.     
  479.     if (Key & CTRL) {
  480.         switch (c) {
  481.     case 'H':
  482.         APPENDTEXT(TEXT312) /* "BkSp" */
  483.         goto all_done;
  484.     case 'I':
  485.         APPENDTEXT(TEXT313) /* "Tab" */
  486.         goto all_done;
  487.     case 'M':
  488.         APPENDTEXT(TEXT314) /* "Enter" */
  489.         goto all_done;
  490.     case '[':
  491.         APPENDTEXT(TEXT315)  /* "Esc" */
  492.         goto all_done;
  493.     default:
  494.         APPENDTEXT(TEXT316) /* "Ctrl+" */
  495.         break;
  496.     }
  497.     }
  498.     
  499.     if (Key & SPEC) {
  500.         switch (c) {
  501.     case '<':
  502.         APPENDTEXT(TEXT317) /* "Home" */
  503.         break;
  504.     case 'N':
  505.         APPENDTEXT(TEXT318) /* "DownArrow" */
  506.         break;
  507.     case 'P':
  508.         APPENDTEXT(TEXT319) /* "UpArrow" */
  509.         break;
  510.     case 'B':
  511.         APPENDTEXT(TEXT320) /* "LeftArrow" */
  512.         break;
  513.     case 'F':
  514.         APPENDTEXT(TEXT321) /* "RightArrow" */
  515.         break;
  516.     case '>':
  517.         APPENDTEXT(TEXT322) /* "End" */
  518.         break;
  519.     case 'Z':
  520.         APPENDTEXT(TEXT323) /* "PageUp" */
  521.         break;
  522.     case 'V':
  523.         APPENDTEXT(TEXT324) /* "PageDown" */
  524.         break;
  525.     case 'C':
  526.         APPENDTEXT(TEXT325) /* "Ins" */
  527.         break;
  528.     case 'D':        
  529.         APPENDTEXT(TEXT326) /* "Del" */
  530.         break;
  531.     case '0':
  532.         APPENDTEXT(TEXT327) /* "F10" */
  533.         break;
  534.     default:    /* single digit function key */
  535.         if ((c < '1') || (c > '9')) return 0; /* unknown! */
  536.         if (TextLength - i < 3) return 0;
  537.         Text[i++] = CHAR328;    /* 'F' */
  538.         Text[i++] = c;
  539.         break;
  540.         }
  541.     }
  542.     else if (c == ' ') APPENDTEXT(TEXT329)  /* "SpaceBar" */
  543.     else {
  544.         /* normal char */
  545.         if (TextLength - i < 2) return 0;
  546.         Text[i++] = c;
  547.     }
  548. all_done:    
  549.     Text[i] = '\0';
  550.     return i;
  551. } /* GetKeyText */
  552.  
  553. /* UpdateMenuItemText:  sets the key binding info in a menu item */
  554. /* ==================                                            */
  555.  
  556. void  PASCAL    UpdateMenuItemText (HMENU hMenu, int Position,
  557.                                     MENUTAB *MTp)
  558. {
  559.     KEYTAB  *KTp;
  560.     char    NewText[MAXMENUTITLE];
  561.     char    OldText[MAXMENUTITLE];
  562.     register int i;
  563.  
  564.     GetMenuString (hMenu, Position, OldText, MAXMENUTITLE, MF_BYPOSITION);
  565.     strcpy (NewText, OldText);
  566.     for (i = 0; (NewText[i] != '\0') && (NewText[i] != '\t'); ++i) ;
  567.         /* find the first tab char or the string's end */
  568.  
  569.     KTp = FindKeyBinding (MTp->m_word & MB_BUF ? (void*)MTp->m_ptr.buf :
  570.                          (void*)MTp->m_ptr.fp);
  571.     if (KTp->k_type != BINDNUL) {
  572.     NewText[i] = '\t';
  573.     if (!GetKeyText (KTp->k_code, &NewText[i+1], MAXMENUTITLE + 1 - i))
  574.         goto no_binding;    /* if out of room, no binding text */
  575.     }
  576.     else {
  577. no_binding:
  578.         /* let's erase the binding text */
  579.     NewText[i] = '\0';
  580.     }
  581.     if (strcmp (NewText, OldText) == 0) return;
  582.     ModifyMenu (hMenu, Position, MF_BYPOSITION | MF_STRING,
  583.                 GetMenuItemID (hMenu, Position), NewText); 
  584. } /* UpdateMenuItemText */
  585.  
  586. /* InitMenuPopup:   perform popup menu init before display (WM_INITMENUPOPUP) */
  587. /* =============                                                              */
  588.  
  589. /* here, we may gray menu entries that are invalid. We also try to
  590.    display a key binding after each menu item */
  591.  
  592. void FAR PASCAL InitMenuPopup (HMENU hMenu, LONG lParam)
  593. {
  594.     int     Position;
  595.     int     ItemCount;
  596.     BOOL    Enable;
  597.     UINT    EntryID;
  598.     register MENUTAB *MTp; /* points the appropriate entry in MenuStaticBind */
  599.     MENUTAB *PrevMTp;      /* to control MenuStaticBind scanning */
  600.  
  601.     if (HIWORD(lParam)) return;     /* do not process the system menu */
  602.  
  603.     ctrlx_key = meta_key = 0;       /* these may have changed since the
  604.                        last call to GetKeyText */
  605.     
  606.     /*-Scan the menu's items */
  607.     ItemCount = GetMenuItemCount (hMenu);
  608.     PrevMTp = &MenuStaticBind[0];
  609.     for (Position =  0; Position < ItemCount; Position++) {
  610.         EntryID = GetMenuEntryID (hMenu, Position);
  611.  
  612.         if (EntryID == 0) continue; /* skip separators */
  613.  
  614.         /*-lookup the item in the menu binding table */
  615.         /* since we assume the items' order follows the binding table,
  616.        we try to optimize the lookup by starting from the last
  617.        found entry */
  618.         if (EntryID >= IDM_POPUP) {
  619.         /* for popup menus MTp yields the nullproc entry */
  620.             MTp = &MenuStaticBind[0];
  621.         }
  622.         else if (EntryID >= IDM_DYNAMIC) {
  623.         /* for dynamic items, MTp is directly set to the proper
  624.            entry in the MenuDynBind */
  625.         MTp = &MenuDynBind[EntryID - IDM_DYNAMIC];
  626.     }
  627.         else {
  628.             MTp = PrevMTp + 1;
  629.         for (;;) {
  630.             if (MTp->m_word == EntryID) {
  631.                 /* found it! */
  632.             PrevMTp = MTp;  /* prepare for next item */
  633.             break;
  634.             }
  635.             if (MTp == PrevMTp) {
  636.                 /* scan completed, item not found! */
  637.                 MTp = &MenuStaticBind[0]; /* use default */
  638.                 break;
  639.             }
  640.             if (MTp->m_word == 0) {
  641.                     MTp = &MenuStaticBind[0];   /* cycle through */
  642.                 }
  643.                 else ++MTp;
  644.         }
  645.     }
  646.  
  647.      /*-for each item found in the binding table, display a key binding */
  648.     if (MTp != &MenuStaticBind[0]) UpdateMenuItemText (hMenu, Position, MTp);
  649.  
  650.     /*-if not quiescent, gray most items */
  651.         if (notquiescent) {
  652.             switch (EntryID) {
  653. #ifdef  IDM_DEBUG
  654.         case IDM_DEBUG:
  655. #endif
  656.         case IDM_QUIT:
  657.         case IDM_CTRLG:
  658.         case IDM_ABOUT:
  659.         case IDM_WHELPINDEX:
  660.         case IDM_WHELPKEYBOARD:
  661.         case IDM_WHELPCOMMANDS:
  662.         case IDM_WHELPPROCEDURES:
  663.         Enable = TRUE;
  664.         break;
  665.         default:
  666.         Enable = FALSE;
  667.         break;
  668.         }
  669.     }
  670.     else {
  671.         /*-if quiescent, gray the invalid items, depending on
  672.            appropriate conditions */
  673.         Enable = TRUE;
  674.             switch (EntryID) {
  675.         case IDM_FILESAVE:
  676.         case IDM_UNMARK:
  677.         /* grayed if current buffer is unmarked */
  678.         if (!(curbp->b_flag & BFCHG)) Enable = FALSE;
  679.         break;
  680.             case IDM_NEXTBUFFER:
  681.         case IDM_USEBUFFER:
  682.         case IDM_KILLBUFFER:
  683.         /* grayed if sole visible buffer */
  684.         if (getdefb () == NULL) Enable = FALSE;
  685.         break;
  686.         case IDM_NARROW:
  687.             /* grayed if already narrowed */
  688.         if (curbp->b_flag & BFNAROW) Enable = FALSE;
  689.             /* and if no mark... */
  690.             case IDM_CUTREGION:
  691.             case IDM_CLIPREGION:
  692.             case IDM_EDIT_REGION_POPUP:
  693.         /* grayed if no mark0 in current window */
  694.         if (curwp->w_markp[0] == NULL) Enable = FALSE;
  695.         break;
  696.             case IDM_REMMARK:
  697.             case IDM_SWAPMARK:
  698.             case IDM_GOTOMARK:
  699.                 /* grayed if no mark at all in current window */
  700.                 {
  701.                     int     i;
  702.                     Enable = FALSE;
  703.                     for (i = 0; i <= 9; i++) if (curwp->w_markp[i]) {
  704.                         Enable = TRUE;
  705.                         break;
  706.                     }
  707.                 }
  708.                 break;
  709.         case IDM_WIDEN:
  710.         /* grayed if not narrowed */
  711.         if (!(curbp->b_flag & BFNAROW)) Enable = FALSE;
  712.         break;
  713.         case IDM_DELWIND:
  714.         case IDM_ONLYWIND:
  715.         case IDM_NEXTWIND:
  716.         case IDM_PREVWIND:
  717.         case IDM_NEXTUP:
  718.         case IDM_NEXTDOWN:
  719.         case IDM_FILE_WINDOW_SIZE_POPUP:
  720.         /* grayed if only one window in current screen */
  721.         if (wheadp->w_wndp == NULL) Enable = FALSE;
  722.         break;
  723.         case IDM_EDIT_CLIPBOARD_POPUP:
  724.         /* grayed if clipboard empty and no mark set */
  725.         if ((curwp->w_markp[0] == NULL) &&
  726.             !IsClipboardFormatAvailable (CF_TEXT)) Enable = FALSE;
  727.         break;
  728.         case IDM_INSERTCLIP:
  729.         /* grayed if clipboard empty */
  730.         if (!IsClipboardFormatAvailable (CF_TEXT)) Enable = FALSE;
  731.         break;
  732.         case IDM_YANK:
  733.         /* grayed if no kill buffer */
  734.         if (kbufh[kill_index] == NULL) Enable = FALSE;
  735.         break;
  736.         case IDM_FORWHUNT:
  737.         case IDM_BACKHUNT:
  738.         /* grayed if no search string */
  739.         if (pat[0] == '\0') Enable = FALSE;
  740.         break;
  741.         case IDM_CTLXE:
  742.         /* grayed if no keyboard macro */
  743.         break;
  744.         case IDM_TILE_VERTICALLY:
  745.             if (!Win31API) Enable = FALSE;
  746.             break;
  747.         }
  748.  
  749.         /*-If view mode, gray the items flagged IDM_NOTVIEW */
  750.         if (curbp->b_mode & MDVIEW) {
  751.             if (!(EntryID & (IDM_DYNAMIC | IDM_POPUP)) &&
  752.                     (MTp->m_word & IDM_NOTVIEW)) Enable = FALSE;
  753.         }
  754.         }
  755.  
  756.         EnableMenuItem (hMenu, Position,
  757.                         MF_BYPOSITION | (Enable ? MF_ENABLED : MF_GRAYED));
  758.     }
  759. } /* InitMenuPopup */
  760.  
  761. /* SimulateExtendedKey:    feed an extended key into the input stream */
  762. /* ===================                                                */
  763.  
  764. static void PASCAL  SimulateExtendedKey (int ec)
  765. {
  766.     char    prefix;
  767.  
  768.     if (in_room(5)) {
  769.     prefix = ec >> 8;
  770.     if (prefix != 0) {
  771.         in_put (0);
  772.         in_put (prefix);
  773.         if (prefix & (MOUS >> 8)) {
  774.         /* in case the key is a mouse action, supply
  775.            dummy mouse position info */
  776.         in_put (1);
  777.         in_put (1);
  778.         }
  779.     }
  780.     in_put (ec & 0xFF);
  781.     }
  782. } /* SimulateExtendedKey */
  783.  
  784. /* MenuCommand: WM_COMMAND message handling */
  785. /* ===========                              */
  786.  
  787. /* returns TRUE if the command has been recognized and FALSE otherwise
  788.    */
  789.  
  790. BOOL FAR PASCAL MenuCommand (UINT wParam, LONG lParam)
  791. {
  792.     FARPROC     ProcInstance;
  793.     DWORD       HelpContext;
  794.  
  795.     switch (LOWORD(wParam)) {
  796.         /* the menu choices from here down to the default statement are
  797.            valid even in the not-quiescent case */
  798.  
  799.     case IDM_ABOUT:
  800.     ProcInstance = MakeProcInstance ((FARPROC)AboutDlgProc,
  801.                      hEmacsInstance);
  802.     DialogBox (hEmacsInstance, "ABOUT", hFrameWnd, ProcInstance);
  803.     FreeProcInstance (ProcInstance);
  804.     break;
  805.     
  806. #ifdef  IDM_DEBUG
  807.     case IDM_DEBUG:     /* places a call to the debugger */
  808.     DebugBreak ();
  809.     break;
  810. #endif
  811.     
  812.     case IDM_QUIT:
  813.     PostMessage (hFrameWnd, WM_CLOSE, 0, 0L);
  814.     break;
  815.     
  816.     case IDM_CTRLG:
  817.     /* this is a special case: we feed the abort key into the input
  818.        stream */
  819.     SimulateExtendedKey (abortc);
  820.     break;
  821.     
  822.     case IDM_WHELPINDEX:
  823.     HelpContext = 0;
  824.     goto InvokeHelp;
  825.     case IDM_WHELPKEYBOARD:
  826.     HelpContext = HELPID_KEYBOARD;
  827.     goto InvokeHelp;
  828.     case IDM_WHELPCOMMANDS:
  829.     HelpContext = HELPID_COMMANDS;
  830.     goto InvokeHelp;
  831.     case IDM_WHELPPROCEDURES:
  832.     HelpContext = HELPID_PROCEDURES;
  833. InvokeHelp:
  834.     WinHelp (hFrameWnd, MainHelpFile,
  835.                  HelpContext ? HELP_CONTEXT : HELP_INDEX, HelpContext);
  836.     MainHelpUsed = TRUE;
  837.     break;
  838.     
  839.     default:
  840.     if (notquiescent) return TRUE;  /* abort processing */
  841.  
  842.     /* the following IDs are processed only if emacs is quiescent */
  843.     switch (LOWORD(wParam)) {
  844.  
  845.     case IDM_GLOBMODE:
  846.     case IDM_MODE:
  847.         ProcInstance = MakeProcInstance ((FARPROC)ModeDlgProc,
  848.                          hEmacsInstance);
  849.         DialogBoxParam (hEmacsInstance, "MODES",
  850.                 hFrameWnd, ProcInstance,
  851.                 (DWORD)(wParam == IDM_GLOBMODE));
  852.         FreeProcInstance (ProcInstance);
  853.         if (wParam = IDM_MODE) {
  854.         upmode ();
  855.         update (FALSE);
  856.         }
  857.         break;
  858.  
  859.     case IDM_FONT:
  860.         PickEmacsFont ();
  861.         break;
  862.  
  863.     case IDM_CASCADE:
  864.         SendMessage (hMDIClientWnd, WM_MDICASCADE, 0, 0L);
  865.         break;
  866.     case IDM_TILE_HORIZONTALLY:
  867.         SendMessage (hMDIClientWnd, WM_MDITILE, MDITILE_VERTICAL, 0L);
  868.         break;
  869.     case IDM_TILE_VERTICALLY:
  870.         SendMessage (hMDIClientWnd, WM_MDITILE, MDITILE_HORIZONTAL, 0L);
  871.         break;
  872.     case IDM_ARRANGEICONS:
  873.         SendMessage (hMDIClientWnd, WM_MDIICONARRANGE, 0, 0L);
  874.         break;
  875.  
  876.     case IDM_NORMALIZE:
  877.         {
  878.         SCREEN  *sp;
  879.  
  880.         sp = (SCREEN*)GetWindowLong ((HWND)(SendMessage (hMDIClientWnd,
  881.                                 WM_MDIGETACTIVE, 0, 0L)),
  882.                                      GWL_SCRPTR);
  883.         newsize (TRUE, sp->s_nrow);
  884.         newwidth (TRUE, sp->s_ncol);
  885.         update (FALSE);
  886.         }
  887.         break;
  888.         
  889.     default:
  890.         if (wParam >= IDM_FIRSTCHILD) return FALSE;
  891.         GenerateMenuSeq (wParam);
  892.         return TRUE;
  893.     }
  894.     }
  895.     
  896.     /* we have processed an internal menu command */
  897.     if (!notquiescent) GenerateMenuSeq (IDM_NULLPROC);
  898.         /* this flushes a possible numeric argument */
  899.     return TRUE;
  900. } /* MenuCommand */
  901.  
  902. /* GetScreenMenuHandle: returns the handle to the 'Screen' menu (for MDI mgt) */
  903. /* ===================                                                        */
  904.  
  905. HMENU FAR PASCAL GetScreenMenuHandle (void)
  906. {
  907.     HMENU   hBaseMenu;
  908.     int     Pos;
  909.  
  910.     hBaseMenu = GetMenu (hFrameWnd);
  911.     for (Pos = GetMenuItemCount (hBaseMenu) - 1; Pos >= 0; Pos--) {
  912.         if (GetMenuEntryID (hBaseMenu, Pos) == IDM_SCREEN_POPUP) {
  913.             return GetSubMenu (hBaseMenu, Pos);
  914.         }
  915.     }
  916.     return NULL;
  917. } /* GetScreenMenuHandle */
  918.  
  919. /* MenuEntryCount:  count of menu entries, excluding the MDI buttons */
  920. /* ==============                                                    */
  921.  
  922. static int PASCAL   MenuEntryCount (HMENU hMenu)
  923. {
  924.     int     Count;
  925.  
  926.     Count = GetMenuItemCount (hMenu);
  927.     if (hMenu == GetMenu (hFrameWnd)) {
  928. #if WINDOW_MSWIN32
  929.         if (GetWindowLong((HWND)SendMessage (hMDIClientWnd, WM_MDIGETACTIVE,
  930.                                              0, 0L), GWL_STYLE) &
  931.             WS_MAXIMIZE) {
  932. #else
  933.         if (HIWORD(SendMessage (hMDIClientWnd, WM_MDIGETACTIVE, 0, 0L))) {
  934. #endif
  935.             /* an MDI child is maximized ==> we have MDI buttons in the
  936.            menu bar */
  937.         Count -= 2;
  938.     }
  939.     }
  940.     return Count;
  941. } /* MenuEntryCount */
  942.  
  943. /* MenuEntryOffset:  position of the first non-MDI button menu entry */
  944. /* ===============                                                   */
  945.  
  946. static int PASCAL   MenuEntryOffset (HMENU hMenu)
  947. {
  948.     if (hMenu == GetMenu (hFrameWnd)) {
  949. #if WINDOW_MSWIN32
  950.         if (GetWindowLong((HWND)SendMessage (hMDIClientWnd, WM_MDIGETACTIVE,
  951.                                              0, 0L), GWL_STYLE) &
  952.             WS_MAXIMIZE) {
  953. #else
  954.         if (HIWORD(SendMessage (hMDIClientWnd, WM_MDIGETACTIVE, 0, 0L))) {
  955. #endif
  956.             /* an MDI child is maximized ==> we have MDI buttons in the
  957.            menu bar */
  958.         return 1;
  959.     }
  960.     }
  961.     return 0;
  962.     
  963. } /* MenuEntryOffset */
  964.  
  965. /* ParseMenu:   parse a piece of menu path */
  966. /* =========                               */
  967.  
  968. static BOOL PASCAL  ParseMenu (char *Name, char *Title, int *Posp)
  969.  
  970. /* Puts the text part of the menu name in Title (at most MAXMENUTITLE
  971.    characters including the \0) and the position number in *Posp. If no
  972.    position is specified, *Posp is set to -1. The returned BOOL is TRUE
  973.    if no error occured and FAILED if a syntax problem was diagnosed */
  974. {
  975.     register int i;
  976.  
  977.     *Posp = -1;
  978.     for (i = 0; Name[i] != '\0'; i++) {
  979.         if (i >= MAXMENUTITLE - 1) {
  980.             mlwrite (TEXT300);  /* "[Incorrect menu]" */
  981.             return FAILED;
  982.         }
  983.         if (Name[i] == '>') break;
  984.         if (Name[i] == '@') {   /* must parse a position index */
  985.             unsigned char *s;
  986.  
  987.             *Posp = 0;
  988.             for (s = &Name[i+1]; (*s != '>') && (*s != '\0'); s++) {
  989.                 if ((*s >= '0') && (*s <= '9')) {
  990.                     *Posp = (*Posp * 10) + (*s - '0');
  991.                 }
  992.                 else {      /* non numerical character! */
  993.                     mlwrite (TEXT300);  /* "[Incorrect menu]" */
  994.                     return FAILED;
  995.                 }
  996.             }
  997.             break;
  998.         }
  999.         Title[i] = Name[i];
  1000.     }
  1001.     Title[i] = '\0';
  1002.     return TRUE;
  1003. } /* ParseMenu */
  1004.  
  1005. /* LocateMenu:  locate a menu entry matching a piece of menu path */
  1006. /* ==========                                                     */
  1007.  
  1008. static BOOL PASCAL  LocateMenu (char *Name, CURMENU *CM)
  1009.  
  1010. /* The returned BOOL is TRUE if a matching menu entry was found, FALSE
  1011.    if no such entry was found and FAILED if a syntax problem was
  1012.    diagnosed. The CURMENU structure is updated if the entry was found.
  1013.    */
  1014. {
  1015.     HMENU   hMenu;
  1016.     int     ItemCount;
  1017.     int     Pos, StartPos, PosOffset;
  1018.     BOOL    Result, DoScan;
  1019.     char    Target[MAXMENUTITLE];
  1020.     char    EntryName[MAXMENUTITLE];
  1021.  
  1022.     while (*Name == '<') {
  1023.     ++Name;
  1024.     --CM->cm_x;
  1025.     CM->cm_pos = -1;
  1026.     }
  1027.     if ((Result = ParseMenu (Name, Target, &Pos)) != TRUE) {
  1028.     return Result;
  1029.     }
  1030.     if (CM->cm_x < 0) {
  1031.     hMenu = GetMenu (hFrameWnd);
  1032.     }
  1033.     else hMenu = CM->cm_parent[CM->cm_x];
  1034.     PosOffset = MenuEntryOffset (hMenu);
  1035.     ItemCount = GetMenuItemCount (hMenu);
  1036.     if (DoScan = (Pos < 0)) Pos = CM->cm_pos;
  1037.     if (Pos < 0) Pos = 0;
  1038.     Pos += PosOffset;
  1039.     StartPos = Pos;
  1040.     do {
  1041.     char    *s;
  1042.  
  1043.     if (IsMenuSeparator (hMenu, Pos)) {
  1044.         EntryName[0] = '\0';
  1045.     }
  1046.     else {
  1047.             GetMenuString (hMenu, Pos, (LPSTR)&EntryName[0],
  1048.                    MAXMENUTITLE, MF_BYPOSITION);
  1049.         if ((s = strchr (EntryName, '\t')) != NULL) *s = '\0';
  1050.             /* discard the accelerator/key-binding info */
  1051.     }
  1052.     if (strcmp (EntryName, Target) == 0) goto MenuLocated;
  1053.         if (!DoScan) {
  1054.             if (*Target == '\0') goto MenuLocated;
  1055.             else break; /* no matching target at indicated position */
  1056.         }
  1057.     if (++Pos >= ItemCount) Pos = 0;
  1058.     } while (Pos != StartPos);
  1059.     return FALSE;   /* not found */
  1060. MenuLocated:
  1061.     CM->cm_pos = Pos - PosOffset;
  1062.     return TRUE;
  1063. } /* LocateMenu */
  1064.  
  1065. /* AddMenuEntry:    add a menu entry (or, recursively, a cascade of entries) */
  1066. /* ============                                                              */
  1067.  
  1068. static BOOL PASCAL  AddMenuEntry (char *Name, UINT ID, CURMENU *CM,
  1069.                                       WORD *MenuType)
  1070. /* the MenuType is a set of flags set returned by this function:
  1071.    MT_DUMMY indicates that the menu item is a mere separator, MT_MENUBAR
  1072.    indicates that the menu bar has been modified */
  1073. {
  1074.     HMENU   hMenu, hPopup;
  1075.     int     Pos;
  1076.     BOOL    Result;
  1077.     char    EntryName[MAXMENUTITLE];
  1078.     WORD    MenuFlags;
  1079.  
  1080.     if ((Result = ParseMenu (Name, EntryName, &Pos)) != TRUE) {
  1081.         return Result;
  1082.     }
  1083.     if (CM->cm_x < 0) {
  1084.     hMenu = GetMenu (hFrameWnd);
  1085.     *MenuType |= MT_MENUBAR;
  1086.     }
  1087.     else hMenu = CM->cm_parent[CM->cm_x];
  1088.     Name = strchr(Name, '>');
  1089.     if (Name != NULL) {     /* our task is to set up a popup menu */
  1090.     if (CM->cm_x >= MAXMENUPARENT - 1) {
  1091.         mlwrite (TEXT301);  /* "[Too many nested popup menus]" */
  1092.         return FALSE;
  1093.     }
  1094.     if ((hPopup = CreatePopupMenu ()) == NULL) {
  1095.         mlwrite (TEXT302);  /* "[lack of resources]" */
  1096.         return ABORT;
  1097.     }
  1098.     MenuFlags = MF_POPUP | MF_STRING;
  1099.     }
  1100.     else {  /* this is a menu entry, not a popup */
  1101.     if (strcmp (EntryName, "-") == 0) {    /* only a separator */
  1102.         MenuFlags = MF_SEPARATOR;
  1103.         *MenuType |= MT_DUMMY;
  1104.         ID = 0;     /* contrary to SDK documentation, InsertMenu
  1105.                does not ignore the ID for a separator in 3.0! */
  1106.     }
  1107.     else {
  1108.         MenuFlags = MF_STRING;
  1109.     }
  1110.     }
  1111.     if (Pos < 0) {
  1112.         Pos = CM->cm_pos + 1;       /* to insert after current entry */
  1113.     if (Pos <= 0) Pos = MenuEntryCount (hMenu); /* to append at end */
  1114.     }
  1115.     Result = InsertMenu (hMenu, Pos + MenuEntryOffset (hMenu),
  1116.                          MenuFlags | MF_BYPOSITION,
  1117.                          Name ? (UINT)hPopup : ID,
  1118.                          (LPSTR)&EntryName[0]);
  1119.     if (!Result) {
  1120.         mlwrite (TEXT302);  /* "[lack of resources]" */
  1121.         Result = FALSE;
  1122.     }
  1123.     else Result = TRUE;
  1124.     if (Name == NULL) {
  1125.         CM->cm_pos = Pos;
  1126.     }
  1127.     else {
  1128.         if (Result != TRUE) {
  1129.             DestroyMenu (hPopup);
  1130.         }
  1131.         else {
  1132.         CM->cm_parent[++CM->cm_x] = hPopup;
  1133.         CM->cm_pos = -1;
  1134.         ++Name;     /* skip the '>' */
  1135.         if ((Result = AddMenuEntry (Name, ID, CM, MenuType)) != TRUE) {
  1136.         /* Menu creation failed at some point, we must undo our own */
  1137.         DeleteMenu (hMenu, Pos + MenuEntryOffset (hMenu),
  1138.                             MF_BYPOSITION);
  1139.         }
  1140.     }
  1141.     }
  1142.     return Result;
  1143. } /* AddMenuEntry */
  1144.  
  1145. /* AddMenuBinding:  bind a menu item to an EPOINTER */
  1146. /* ==============                                   */
  1147.  
  1148. static BOOL PASCAL  AddMenuBinding (ETYPE EPOINTER eptr, WORD type)
  1149.  
  1150. /* called by bindtomenu and macrotomenu, once the function or macro name
  1151.    has been parsed */
  1152. {
  1153.     BOOL    Result;
  1154.     WORD    MenuType = 0;   /* info returned by AddMenuEntry */
  1155.     char    FullName[NSTRING];
  1156.     char    *Name;
  1157.     UINT    DynIndex;
  1158.     CURMENU CM;
  1159.  
  1160.     /*-Let's make sure we have a free entry in the binding table */
  1161.     for (DynIndex = 0; MenuDynBind[DynIndex].m_word != 0; DynIndex++) {
  1162.         if (DynIndex >= MAXDYNMENU) {
  1163.             mlwrite (TEXT17);
  1164.             /* "Binding table FULL!" */
  1165.             return FALSE;
  1166.         }
  1167.     }
  1168.  
  1169.     /*-Parse the menu Name */
  1170.     Name = &FullName[0];
  1171.     if ((Result = mlreply (TEXT306, Name, NSTRING)) != TRUE) {
  1172.             /* "Menu: " */
  1173.         return Result;
  1174.     }
  1175.     if (*Name == '>') {
  1176.         CM.cm_pos = -1;
  1177.         CM.cm_x = -1;
  1178.         ++Name;
  1179.     }
  1180.     else CM = CurrentMenu;
  1181.     while ((Result = LocateMenu (Name, &CM)) == TRUE) {
  1182.         /* ...looping until we hit a piece of the menu path that does
  1183.        not yet exist */
  1184.     HMENU   hMenu;
  1185.     
  1186.     if (CM.cm_x < 0) hMenu = GetMenu (hFrameWnd);
  1187.     else hMenu = CM.cm_parent[CM.cm_x];
  1188.     hMenu = GetSubMenu (hMenu, CM.cm_pos + MenuEntryOffset (hMenu));
  1189.     if (hMenu == NULL) {
  1190.         /* this is not a popup menu, we loose! */
  1191.         mlwrite (TEXT300);  /* "[Incorrect menu]" */
  1192.         return FALSE;
  1193.     }
  1194.     if (CM.cm_x >= MAXMENUPARENT - 1) {
  1195.         mlwrite (TEXT301);  /* "[Too many nested popup menus]" */
  1196.         return FALSE;
  1197.     }
  1198.     CM.cm_parent[++CM.cm_x] = hMenu;
  1199.     CM.cm_pos = -1;
  1200.     Name = strchr (Name, '>');
  1201.     if (Name == NULL) {
  1202.         /* premature end of menu path! */
  1203.         mlwrite (TEXT300);  /* "[Incorrect menu]" */
  1204.         return FALSE;
  1205.     }
  1206.     ++Name;     /* skip the '>' */
  1207.     }
  1208.     if (Result != FALSE) return Result;  /* syntax error, no cigar! */
  1209.  
  1210.     /*-Create the menu entry (or cascade of entries) */
  1211.     if ((Result = AddMenuEntry (Name, DynIndex + IDM_DYNAMIC, &CM,
  1212.                                 &MenuType)) != TRUE) {
  1213.         return Result;
  1214.     }
  1215.     CurrentMenu = CM;
  1216.  
  1217.     if (!(MenuType & MT_DUMMY)) {
  1218.     /*-complete the binding */
  1219.     MenuDynBind[DynIndex].m_word = type;
  1220.     MenuDynBind[DynIndex].m_ptr = eptr;
  1221.     }
  1222.     
  1223.     if (MenuType & MT_MENUBAR) DrawMenuBar (hFrameWnd);
  1224.     return TRUE;
  1225. } /* AddMenuBinding */
  1226.  
  1227. /* bindtomenu:  bind a menu item to an emacs function */
  1228. /* ==========                                         */
  1229.  
  1230. PASCAL bindtomenu (int f, int n)
  1231. /* command arguments IGNORED */
  1232. {
  1233.     ETYPE EPOINTER  e;
  1234.  
  1235.     e.fp = getname (TEXT304);   /* "Function: " */
  1236.     if (e.fp == NULL) {
  1237.         mlwrite (TEXT16);       /* "[No such function]" */
  1238.         return FALSE;
  1239.     }
  1240.     return AddMenuBinding (e, MB_FNC);
  1241. } /* bindtomenu */
  1242.  
  1243. /* macrotomenu: bind a menu item to a macro (i.e. a buffer) */
  1244. /* ===========                                              */
  1245.  
  1246. PASCAL macrotomenu (int f, int n)
  1247. /* command arguments IGNORED */
  1248. {
  1249.     ETYPE EPOINTER  e;
  1250.     char    Name[NBUFN];
  1251.     BOOL    Result;
  1252.  
  1253.     if ((Result = mlreply(TEXT305, &Name[1], NBUFN-2)) != TRUE) {
  1254.             /* "Macro: " */
  1255.         return Result;
  1256.     }
  1257.     Name[0] = '[';
  1258.     strcat (Name, "]");
  1259.     if ((e.buf = bfind (Name, FALSE, 0)) == NULL) {
  1260.         mlwrite (TEXT130);
  1261.         /* "Macro not defined" */
  1262.         return FALSE;
  1263.     }
  1264.     return AddMenuBinding (e, MB_BUF);
  1265. } /* macrotomenu */
  1266.  
  1267. /* DeleteMenuBinding:   remove a menu entry and its binding or sub-entries */
  1268. /* =================                                                       */
  1269.  
  1270. static BOOL PASCAL  DeleteMenuBinding (HMENU hMenu, int Pos)
  1271. /* returns TRUE except when an attempt is made to delete the 'Screen'
  1272.    menu */
  1273. {
  1274.     BOOL    Result;
  1275.     UINT    ID;
  1276.     HMENU   hPopup;
  1277.  
  1278.     Pos += MenuEntryOffset (hMenu);
  1279.     if (!IsMenuSeparator (hMenu, Pos)) {
  1280.         if ((ID = GetMenuItemID (hMenu, Pos)) != -1) {
  1281.             /* this is a pure menu item */
  1282.             if (ID >= IDM_DYNAMIC) {
  1283.                 /* let's free the dynamic binding table entry */
  1284.                 MenuDynBind[ID - IDM_DYNAMIC].m_word = 0;
  1285.             }
  1286.         }
  1287.         else if ((hPopup = GetSubMenu (hMenu, Pos)) != NULL) {
  1288.             /* this is a popup, we must delete all the contained entries */
  1289.             int     i;
  1290.  
  1291.             if (ID == IDM_SCREEN_POPUP) {
  1292.                 /* this menu is used by the system's MDI manager to list the
  1293.                MDI childs. We cannot remove it */
  1294.                 return FALSE;
  1295.             }
  1296.             for (i = MenuEntryCount (hPopup); i > 0;) {
  1297.                 DeleteMenuBinding (hPopup, --i);
  1298.             }
  1299.         }
  1300.     }
  1301.     DeleteMenu (hMenu, Pos, MF_BYPOSITION);
  1302.     if (hMenu == GetMenu (hFrameWnd)) DrawMenuBar (hFrameWnd);
  1303.     return TRUE;
  1304. } /* DeleteMenuBinding */
  1305.  
  1306. /* unbindmenu:  remove a menu entry */
  1307. /* ==========                       */
  1308.  
  1309. PASCAL unbindmenu (int f, int n)
  1310. /* command arguments IGNORED */
  1311. {
  1312.     BOOL    Result;
  1313.     char    FullName[NSTRING];
  1314.     char    *Name;
  1315.     CURMENU CM;
  1316.  
  1317.     Name = &FullName[0];
  1318.     if ((Result = mlreply (TEXT303, Name, NSTRING)) != TRUE) {
  1319.             /* "Menu: " */
  1320.         return Result;
  1321.     }
  1322.     if (*Name == '>') {
  1323.         CM.cm_pos = -1;
  1324.         CM.cm_x = -1;
  1325.         ++Name;
  1326.     }
  1327.     else CM = CurrentMenu;
  1328.     while ((Result = LocateMenu (Name, &CM)) == TRUE) {
  1329.     HMENU   hMenu;
  1330.     
  1331.     if (CM.cm_x < 0) hMenu = GetMenu (hFrameWnd);
  1332.     else hMenu = CM.cm_parent[CM.cm_x];
  1333.     Name = strchr (Name, '>');
  1334.     if (Name != NULL) {     /* the menu path continues beyond this... */
  1335.         hMenu = GetSubMenu (hMenu, CM.cm_pos + MenuEntryOffset (hMenu));
  1336.         if (hMenu == NULL) {
  1337.             /* this is not a popup menu, we loose! */
  1338.             Result = FALSE;
  1339.             break;
  1340.         }
  1341.         if (CM.cm_x >= MAXMENUPARENT - 1) {
  1342.         mlwrite (TEXT301);  /* "[Too many nested popup menus]" */
  1343.         return FALSE;
  1344.         }
  1345.         CM.cm_parent[++CM.cm_x] = hMenu;
  1346.         CM.cm_pos = 0;
  1347.         ++Name;     /* skip the '>' */
  1348.         /* and loop once again... */
  1349.     }
  1350.     else {                  /* this is it: the end of the menu path */
  1351.         int     Pos;
  1352.  
  1353.         if ((Result = DeleteMenuBinding (hMenu, CM.cm_pos)) != TRUE) {
  1354.             return FALSE;
  1355.         }
  1356.         if ((Pos = MenuEntryCount (hMenu) - 1) < CM.cm_pos) {
  1357.         /* we just deleted the item at the menu's end. This
  1358.            position is no longer valid */
  1359.         CM.cm_pos = Pos;
  1360.         }
  1361.         CurrentMenu = CM;
  1362.         break;      /* we are done */
  1363.     }
  1364.     }
  1365.     /* we arrive here if the menu was not found or a syntax error occured */
  1366.     if (Result == FALSE) mlwrite (TEXT300); /* "[Incorrect menu]" */
  1367.     return Result;
  1368. } /* unbindmenu */
  1369.